package com.zszedu.web.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zszedu.constants.RedisKeyConstants;
import com.zszedu.pojo.User;
import com.zszedu.service.UserService;
import com.zszedu.utils.HttpClientUtils;
import com.zszedu.utils.MD5Util;
import com.zszedu.utils.UUIDUtil;
import com.zszedu.vo.RealName;
import com.zszedu.web.config.JDWXRealNameConfig;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@RestController
@RequestMapping("user")
public class UserController {
    @DubboReference
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JDWXRealNameConfig jdwxRealNameConfig;


    //验证手机号以及短信是否正确
    public void queryPhoneAndCode(String phone,String code){
        // 格式验证
        if (!Pattern.matches("^1[3-9]\\d{9}$", phone)) {
            throw new RuntimeException("手机格式不正确！");
        }
        //判断验证码是否正常
        //根据手机号获取存储在Redis中的验证码
        String key = String.format(RedisKeyConstants.SMS_REG, phone);
        String realCode = (String) redisTemplate.opsForValue().get(key);

        //如果该验证码为空，则用户在获取验证码后，又更换了手机号
        //如果内容跟存储在Redis中的数据不相同则用户输入的验证码错误
        if (realCode == null || !realCode.equals(code)) {
            throw new RuntimeException("验证码不正确！");
        }

    }

    //注册(添加)
    @PostMapping("register")
    public Map register(String phone, String pwd, String code) {
       queryPhoneAndCode(phone,code);

        Boolean addUser = userService.addUser(phone, pwd);
        return new HashMap() {{
            put("success", addUser);
        }};
    }

    //根据手机号查询用户
    @GetMapping("check/{phone}")
    public Boolean checkPhone(@PathVariable String phone) {
        User user = userService.queryPhone(phone);
        return user == null;
    }

    //返回所有用户数量
    @GetMapping("count")
    public Integer getCount() {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        ValueOperations forValue = redisTemplate.opsForValue();
        Integer count = (Integer) forValue.get(RedisKeyConstants.REDIS_COUNT);
        //为空则Redis中不存在该数据，存入数据
        if (count == null) {
            // 存储时间为 1 小数
            count = userService.queryCount();
            forValue.set(RedisKeyConstants.REDIS_COUNT, count, 1, TimeUnit.HOURS);
        }
        return count;
    }

    //登录，enter 长度是 4 位 就是验证码 ，大于 4 位则是密码
    // 根据前端发过来的手机号和密码/验证码 查询用户是否存在
    //优化，短信验证登录
    @PostMapping("loginForPwd")
    public Map loginForPwd(String phone,String enter){
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        ValueOperations forValue = redisTemplate.opsForValue();
        User user = null;
        //密码长度最低 6 位
        //验证码长度必须是 4 位，长度 = 4 则是短信验证码
        if (enter.length() == 4){

            queryPhoneAndCode(phone,enter);

            //能执行到这里说明验证码正确，通过手机号获取用户
            user = userService.queryPhone(phone);
        } else {
            //通过密码查找用户
            user = userService.queryUser(phone, enter);
        }
        //能运行到这里说明该用户存在

        //更新上次登录时间
        userService.editTime(user.getId());

        // 使用Java官方的类，生成UUID作为一个令牌(token)，将这个token作为用户标识，当该信息存在则说明用户是登录过的
        String token = UUIDUtil.getUUID();
        //使用 token作为 Key 值 user 作为 Value 值，存储在Redis中
        String key = "login:" + token;
        forValue.set(key,user);//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        Map map = new HashMap();
        map.put("success",true);
        map.put("token", token);
        map.put("loginUser", user);//将用户数据传入前端，登录完成后会使用到

        return map;
    }

    //忘记密码
    @PostMapping("forget")
    public Map forget(String phone, String pwd, String code) {
        queryPhoneAndCode(phone,code);

        //判断手机号是以注册过，如果用户不存在则该手机号没有注册过
        if (userService.queryPhone(phone) == null) {
            throw new RuntimeException("用户不存在");
        }
        Boolean editUser = userService.editUser(phone, pwd);
        return new HashMap() {{
            put("success", editUser);
        }};
    }


    //实名验证
    @GetMapping("authentication/{name}/{card}/{phone}/{code}")
    public Map authentication(@PathVariable String name,
                              @PathVariable String card,
                              @PathVariable String phone,
                              @PathVariable String code,
                              @RequestHeader("uphone")  String uphone) throws Exception {
        queryPhoneAndCode(phone,code);

        //判断用户绑定的手机号码，与登录时携带的用户信息手机号码是否一致
        if (userService.queryPhone(uphone) != null && !phone.equals(uphone)){
            throw new RuntimeException("绑定用户的手机号码错误！");
        }

        //第三方接口所需要的参数 ，姓名/身份证/appkey钥匙
        HashMap params = new HashMap();
        params.put("name",name);
        params.put("certNo",card);
        params.put("appkey",jdwxRealNameConfig.getAppkey());
        //跨域问题，给与 连接接口地址
        String result = HttpClientUtils.doGet(jdwxRealNameConfig.getUrl(),params);
        //通过 ObjectMapper 将接口返回的结果转换成对应的对象
        ObjectMapper mapper = new ObjectMapper();
        RealName realName = mapper.readValue(result, RealName.class);

        //返回的 success 是个字符串，只不过内容是T/F，
        Boolean success = Boolean.parseBoolean(realName.getResult().get("success"));

        //当返回结果失败就会进入判断语句，抛出异常
        if (!success){
            throw new RuntimeException("身份证与姓名不匹配");
        }
        //运行到这里表示该接口返回结果正常，更新用户信息
        Boolean real = userService.editUserReal( name, card, phone);
        return new HashMap(){{
           put("success",real);
        }};
    }

    //退出，清除保存在Redis中的数据
    @GetMapping("logout")
    public Boolean logOut(@RequestHeader("token") String token){
        String key = "login:" + token;
        Boolean delete = redisTemplate.delete(key);
        return true;
    }

    //上传头像路径
    //MultipartFile = 文件上传对象,对象的名称必须与前端 FormData() 对象名称一致否则赋值失败
    //@RequestHeader("uid") = 从前端获取存储在浏览器中的用户id
    @PostMapping("upload")
    public String upload(MultipartFile headImage ,@RequestHeader("uid") Integer uid) throws IOException {
        //获取用户传入的原始文件名
        String filename = headImage.getOriginalFilename();
        // 随机命名  img12313——1.jpg
        filename = "img" + UUIDUtil.getUUID() + "_" + filename;

        //创建创建文件夹(保存在服务器端)
        File file = new File("d:/JAVA/imp/");
        file.mkdir();//创建文件夹，该文件不存在创建，存在则无事发生

        //根据parent抽象路径和child路径名字符串，创建一个新的File实例
        File pos = new File(file + "/" + filename);
        //使用transferTo()方法将上传文件写到服务器上指定的文件中;
        headImage.transferTo(pos);
        //必须添加路径，否则响应方法接收不到
        String savePath = "http://127.0.0.1/user/" + filename;
        //修改用户表中的头像路径字段
        userService.editHeaderImage(uid,savePath);

        //返回的是一个字符串， "/user/imgxxxxxxxxx-xxx.jpg"
        return savePath;
    }

    //接收上方 （upload）返回的响应
    @GetMapping("img{filename}")
    public void respImg(HttpServletResponse response,@PathVariable String filename) throws Exception {
        //创建字节输入流,获取存储在硬盘上的图片信息
        InputStream in = new FileInputStream("d:/JAVA/imp/img" + filename);
        //使用响应对象，获取字节输出流
        OutputStream out = response.getOutputStream();

        byte[] bytes = new byte[1024];//设置字节大小，准备循环写入
        int len = 0;//从0开始写入
        //遍历，写入给前端数据中
        while ((len  = in.read(bytes)) != -1){
            out.write(bytes,0,len);
        }
        //关闭流
        out.close();
        in.close();
    }
}
